package com.lee.service.impl;


import com.lee.service.SSEService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
public class SSEServiceImpl implements SSEService {
    // 用于存储用户ID和对应的SseEmitter对象的映射
    private static final Map<String,SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();

    /**
     * 创建一个新的SseEmitter对象，并进行相关配置，将其存储到映射中
     *
     * @param uid 用户ID，用于唯一标识一个用户
     * @return 创建好的SseEmitter对象
     */
    @Override
    public SseEmitter crateSse(String uid) {
        // 创建一个SseEmitter对象，设置超时时间为0L，表示不超时
        SseEmitter sseEmitter = new SseEmitter(0L);
        // 当SSE连接正常完成时的回调函数
        sseEmitter.onCompletion(() -> {
            // 记录日志，表明该用户的连接已结束
            log.info("[{}]结束链接", uid);
            // 从映射中移除该用户对应的SseEmitter对象
            sseEmitterMap.remove(uid);
        });
        // 当SSE连接超时时的回调函数
        sseEmitter.onTimeout(() -> {
            // 记录日志，表明该用户的连接超时
            log.info("[{}]链接超时", uid);
        });
        // 当SSE连接发生异常时的回调函数
        sseEmitter.onError(throwable -> {
            try {
                // 记录日志，表明该用户的连接发生异常，并输出异常信息
                log.info("[{}]链接异常，{}", uid, throwable.toString());
                // 向客户端发送一个异常事件，包含事件ID、事件名称、事件数据和重连时间
                sseEmitter.send(SseEmitter.event()
                        .id(uid)
                        .name("发生异常")
                        .data("发生异常请重试")
                        .reconnectTime(3000));
                // 将该用户对应的SseEmitter对象重新放入映射中
                sseEmitterMap.put(uid, sseEmitter);
            } catch (IOException e) {
                // 打印异常堆栈信息
                e.printStackTrace();
            }
        });
        try {
            // 向客户端发送一个初始事件，设置重连时间为5000毫秒
            sseEmitter.send(SseEmitter.event().reconnectTime(5000));
        } catch (IOException e) {
            // 打印异常堆栈信息
            e.printStackTrace();
        }
        // 将该用户对应的SseEmitter对象放入映射中
        sseEmitterMap.put(uid, sseEmitter);
        // 记录日志，表明该用户的SSE连接创建成功
        log.info("[{}]创建sse连接成功!", uid);
        // 返回创建好的SseEmitter对象
        return sseEmitter;
    }
    /**
     * 向指定用户发送消息
     *
     * @param uid       用户ID，用于唯一标识一个用户
     * @param messageId 消息ID，用于唯一标识一条消息
     * @param message   要发送的消息内容
     * @return 消息发送成功返回true，失败返回false
     */
    @Override
    public boolean sendMessage(String uid, String messageId, String message) {
        // 检查消息内容是否为空
        if ("".equals(message) || message == null) {
            // 记录日志，表明该用户的消息参数异常，消息内容为空
            log.info("[{}]参数异常，msg为空", uid);
            // 返回false，表示消息发送失败
            return false;
        }
        // 从映射中获取该用户对应的SseEmitter对象
        SseEmitter sseEmitter = sseEmitterMap.get(uid);
        // 检查该用户对应的SseEmitter对象是否存在
        if (sseEmitter == null) {
            // 记录日志，表明该用户的SSE连接不存在
            log.info("[{}]sse连接不存在", uid);
            // 返回false，表示消息发送失败
            return false;
        }
        try {
            // 向客户端发送消息事件，包含事件ID、重连时间和消息数据
            sseEmitter.send(SseEmitter.event().id(messageId).reconnectTime(60000).data(message));
            // 记录日志，表明消息推送成功
            log.info("用户{},消息ID：{}，推送成功：{}", uid, messageId, message);
            // 返回true，表示消息发送成功
            return true;
        } catch (IOException e) {
            // 从映射中移除该用户对应的SseEmitter对象
            sseEmitterMap.remove(uid);
            // 记录日志，表明消息推送失败
            log.info("用户{},消息ID：{}，消息推送失败：{}", uid, messageId, message);
            // 标记SseEmitter对象已完成
            sseEmitter.complete();
            // 返回false，表示消息发送失败
            return false;
        }
    }
    /**
     * 关闭指定用户的SSE连接
     *
     * @param uid 用户ID，用于唯一标识一个用户
     */
    @Override
    public void closeSse(String uid) {
        // 检查映射中是否包含该用户对应的SseEmitter对象
        if (sseEmitterMap.containsKey(uid)) {
            // 从映射中获取该用户对应的SseEmitter对象
            SseEmitter sseEmitter = sseEmitterMap.get(uid);
            // 标记SseEmitter对象已完成
            sseEmitter.complete();
            // 从映射中移除该用户对应的SseEmitter对象
            sseEmitterMap.remove(uid);
        } else {
            // 记录日志，表明该用户的连接已关闭
            log.info("用户{}连接已关闭", uid);
        }
    }
}
